﻿using System;
using System.Collections.Generic;
using System.Configuration;
using Application.MainModule.DinnerManagement;
using Application.MainModule.ResourceManagement;
using Application.MainModule.UserManagement;
using Domain.MainModule.Repositories;
using Infrastructure.CrossCutting.Caching;
using Infrastructure.CrossCutting.IoC;
using Infrastructure.CrossCutting.Resource;
using Infrastructure.Data.Core;
using Infrastructure.Data.MainModule.MainModule;
using Infrastructure.Data.MainModule.Repositories;
using Infrastructure.Data.MainModule.UnitOfWork;
using Microsoft.Practices.Unity;
using Presentation.Web.UI.Authentication.FormsAuthenticationParty;
using Presentation.Web.UI.Authentication.OpenIdRelyingParty;
using Presentation.Web.UI.BootStrapper.Unity.LifetimeManagers;
using Unity.Wcf;

namespace Presentation.Web.UI.BootStrapper.Unity
{
    /// <summary>
    /// Implemented container in Microsoft Practices Unity
    /// </summary>
    sealed class IoCUnityContainer : UnityServiceHostFactory, IContainer
    {
        #region Members

        readonly IDictionary<string, IUnityContainer> _containersDictionary;
        #endregion

        #region Constructor

        /// <summary>
        /// Create a new instance of IoCUnitContainer
        /// </summary>
        public IoCUnityContainer()
        {
            _containersDictionary = new Dictionary<string, IUnityContainer>();

            //Create root container
            IUnityContainer rootContainer = new UnityContainer();
            _containersDictionary.Add("RootContext", rootContainer);

            //Create container for real context, child of root container
            IUnityContainer realAppContainer = rootContainer.CreateChildContainer();
            _containersDictionary.Add("RealAppContext", realAppContainer);

            //Create container for testing, child of root container
            IUnityContainer fakeAppContainer = rootContainer.CreateChildContainer();
            _containersDictionary.Add("FakeAppContext", fakeAppContainer);

            IUnityContainer wcfAppContainer = rootContainer.CreateChildContainer();
            _containersDictionary.Add("WcfAppContext", wcfAppContainer);

            ConfigureRootContainer(rootContainer);
            ConfigureRealContainer(realAppContainer);
            ConfigureFakeContainer(fakeAppContainer);
            ConfigureContainer(wcfAppContainer);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Configure root container.Register types and life time managers for unity builder process
        /// </summary>
        /// <param name="container">Container to configure</param>
        void ConfigureRootContainer(IUnityContainer container)
        {
            //Take into account that Types and Mappings registration could be also done using the UNITY XML configuration
            //But we prefer doing it here (C# code) because we'll catch errors at compiling time instead execution time, 
            //if any type has been written wrong.

        }

        /// <summary>
        /// Configure real container. Register types and life time managers for unity builder process
        /// </summary>
        /// <param name="container">Container to configure</param>
        void ConfigureRealContainer(IUnityContainer container)
        {

        }

        /// <summary>
        /// Configure fake container.Register types and life time managers for unity builder process
        /// </summary>
        /// <param name="container">Container to configure</param>
        void ConfigureFakeContainer(IUnityContainer container)
        {
            //Note: Generic register type method cannot be used here, 
            //MainModuleFakeContext cannot have implicit conversion to IMainModuleContext

            //container.RegisterType(typeof(IMainModuleUnitOfWork), typeof(FakeMainModuleUnitOfWork), new PerExecutionContextLifetimeManager());
        }

        #endregion

        #region IServiceFactory Members
        public TService Resolve<TService>()
        {
            //We use the default container specified in AppSettings
            string containerName = ConfigurationManager.AppSettings["defaultIoCContainer"];

            if (String.IsNullOrEmpty(containerName)
                ||
                String.IsNullOrWhiteSpace(containerName))
            {
                throw new ArgumentNullException("DefaultIOCSettings");
            }

            if (!_containersDictionary.ContainsKey(containerName))
                throw new InvalidOperationException("ContainerNotFound");

            IUnityContainer container = _containersDictionary[containerName];

            return container.Resolve<TService>();
        }

        public object Resolve(Type type)
        {
            //We use the default container specified in AppSettings
            string containerName = ConfigurationManager.AppSettings["defaultIoCContainer"];

            if (String.IsNullOrEmpty(containerName) || String.IsNullOrWhiteSpace(containerName))
            {
                throw new ArgumentNullException("DefaultIOCSettings");
            }

            if (!_containersDictionary.ContainsKey(containerName))
                throw new InvalidOperationException("ContainerNotFound");

            IUnityContainer container = _containersDictionary[containerName];

            return container.Resolve(type, null);
        }

        
        public void RegisterType(Type type)
        {
            IUnityContainer container = this._containersDictionary["RootContext"];

            if (container != null)
                container.RegisterType(type, new TransientLifetimeManager());
        }

        #endregion

        protected override void ConfigureContainer(IUnityContainer container)
        {

          
            container.RegisterType<IRSVPRepository, RSVPRepository>(new TransientLifetimeManager());
            container.RegisterType<IDinnerRepository, DinnerRepository>(new TransientLifetimeManager());
            container.RegisterType<ICustomerRepository, CustomerRepository>(new TransientLifetimeManager());
            container.RegisterType<IBlurbRepository, BlurbRepository>(new TransientLifetimeManager());
            container.RegisterType<ITranslationRepository, TranslationRepository>(new TransientLifetimeManager());
            //Register application services mappings
            container.RegisterType<IFormsAuthentication, FormsAuthenticationService>(new TransientLifetimeManager());
            container.RegisterType<IOpenIdRelyingPartyService, OpenIdRelyingPartyService>(new TransientLifetimeManager());
            container.RegisterType<IUserManagementService, UserManagementService>(new TransientLifetimeManager());
            container.RegisterType<IDinnerManagementService, DinnerManagementService>(new TransientLifetimeManager());
            container.RegisterType<IDBResourceSerivce, LanguageService>(new TransientLifetimeManager());

            //Register crosscuting mappings
            container.RegisterInstance<ILocalCache>(new AspNetCache(), new ContainerControlledLifetimeManager())
               .RegisterInstance<IDistributedCache>(MemcachedCache.Create(), new ContainerControlledLifetimeManager())
               .RegisterInstance<ICache>(container.Resolve<CompositeCache>(), new ContainerControlledLifetimeManager());
            container.RegisterType<IQueryableUnitOfWork, MainModuleUnitOfWork>(new TransientLifetimeManager());
            container.RegisterInstance(new MainModuleContext(), new PerExecutionContextLifetimeManager());
        }
    }
}
